<!DOCTYPE html>
<html lang="en">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>blockEditor</title>
<uieip type="file" value="inc" src="/yqwin/easyui/include.txt" cache="true" minute="1440"></uieip>
<uieip type="file" value="css" src="/yqwin/css/yqwin.min" cache="false"></uieip>
<uieip type="file" value="js" src="/yqwin/data/yqwin.min" cache="false"></uieip>
<script src="/scripts/gojs/go.js"></script>
<script src="/scripts/gojs/extensions/Figures.js"></script>
<script src="/scripts/gojs/extensions/DrawCommandHandler.js"></script>
<style>
  body {
    background-color: rgb(253, 239, 239);
  }
</style>

<body>
  <a href="javascript:window.history.back();">返回</a>
  <div id="myDiagramDiv"
    style="border: 1px solid black; width: 100%; height: 600px; position: relative; cursor: auto; font: 13px sans-serif;">
  </div>
  <div id="buttons">
    <button onclick="isEdit(true)">enabled</button>
    <button onclick="isEdit(false)">disable</button>
    <button onclick="getValue()">getValue</button>
    <button onclick="setValue()">setValue</button>
    <button onclick="setClear()">clear</button>
    <button onclick="destroy()">destroy</button>
    <button onclick="reinit()">reinit</button>
  </div>
  <textarea id="mySavedModel" style="width: 100%; height: 300px"></textarea>

  <script>
    $(function () {
      $("#myDiagramDiv").yqDiagram({
        value: defaultVal
      });
    });

    function isEdit(tf) {
      $("#myDiagramDiv").yqDiagram("isEdit", tf);
    }
    function getValue() {
      $('#mySavedModel').val(JSON.stringify($("#myDiagramDiv").yqDiagram("getValue"), null, 2));
      //opt.diagram.isModified = false;
    }
    function setValue() {
      $("#myDiagramDiv").yqDiagram("setValue", $('#mySavedModel').val());
    }
    function setClear() {
      $("#myDiagramDiv").yqDiagram("clear");
    }
    function destroy() {
      $("#myDiagramDiv").yqDiagram("destroy");
    }
    function reinit() {
      $("#myDiagramDiv").yqDiagram({
        value: defaultVal
      });
    }

    var defaultVal = {
      "nodes": [
        { "category": "block", "text": "Find Problem", "key": -9, "loc": "-20 -140", "color": "#3358ff", "fill": "white", "figure": "Procedure", "thickness": 3 },
        { "category": "block", "text": "What do we want?", "key": -10, "loc": "-110 -320", "group": -16, "figure": "Ellipse", "fill": "white" },
        { "category": "block", "text": "What do our users want?", "key": -11, "loc": "70 -320", "group": -20, "figure": "Ellipse", "fill": "#ffffff", "color": "black" },
        { "category": "block", "text": "Meetings", "key": -12, "loc": "-110 -440", "group": -16, "figure": "TriangleDown", "fill": "#ffffff" },
        { "category": "block", "text": "Reviews", "key": -13, "loc": "70 -440", "group": -20, "figure": "TriangleDown", "fill": "#ffffff", "color": "black" },
        { "category": "block", "text": "Can we solve it?", "key": -14, "loc": "190 -140", "color": "#7d33ff", "fill": "#ffffff", "figure": "Diamond", "size": "140 80", "thickness": 3 },
        { "category": "block", "text": "Design Prototype", "key": -7, "loc": "337.191 -440", "color": "black", "fill": "#f2dfe0", "group": -17, "figure": "Procedure", "size": "140 80" },
        { "category": "block", "text": "Does it work?", "key": -8, "loc": "337.191 -300", "color": "black", "fill": "#f2dfe0", "group": -17, "figure": "Diamond", "size": "140 80" },
        { "category": "block", "text": "Market/Sell\nSolution", "key": -15, "loc": "470 -157.5", "color": "#25ad23", "fill": "#ffffff", "group": -19, "figure": "Rectangle", "size": "120 60" },
        { "isGroup": true, "category": "base", "text": "Problem-Solving Process", "key": -17, "loc": "365.40458203125 -370", "fill": "#fcbbbd", "thickness": 3, "color": "#000000" },
        { "category": "block", "text": "Implement \nSolution", "key": -18, "loc": "470 -82.5", "color": "#25ad23", "fill": "#ffffff", "group": -19, "figure": "Rectangle", "size": "120 60" },
        { "isGroup": true, "category": "base", "text": "Internal", "key": -16, "loc": "-110 -380", "fill": "#d5ebff", "dash": null, "thickness": 1, "group": -22 },
        { "isGroup": true, "category": "base", "text": "External", "key": -20, "loc": "70 -380", "fill": "#d5ebff", "dash": null, "thickness": 1, "group": -22 },
        { "isGroup": true, "category": "base", "text": "Distribute Solution", "key": -19, "loc": "470 -120", "fill": "#b3e6b3", "thickness": 2, "dash": [4, 4] },
        { "category": "block", "text": "Yes!", "key": -21, "loc": "350 -120", "size": "60 40", "figure": "ManualOperation", "fill": "#ffffff", "color": "#25ad23", "thickness": 3 },
        { "isGroup": true, "category": "base", "text": "Sources", "key": -22, "loc": "-20 -388.66328125", "fill": "#a5d2fa", "dash": [4, 4], "color": "#3358ff" }
      ],
      "links": [
        { "from": -12, "to": -10, "points": [-110, -410, -110, -400, -110, -380, -110, -380, -110, -360, -110, -350], "dash": null, "dir": 1 },
        { "from": -13, "to": -11, "points": [70, -410, 70, -400, 70, -380, 70, -380, 70, -360, 70, -350], "dash": null, "color": "#000000", "dir": 1 },
        { "from": -10, "to": -9, "points": [-110, -290, -110, -280, -110, -230, -40, -230, -40, -180, -40, -170], "dir": 2, "dash": [4, 4] },
        { "from": -11, "to": -9, "points": [70, -290, 70, -280, 70, -230, 0, -230, 0, -180, 0, -170], "dash": [4, 4], "dir": 2 },
        { "from": -9, "to": -14, "points": [40, -140, 50, -140, 80, -140, 80, -140, 110, -140, 120, -140], "dir": 1, "color": "#3358ff" },
        { "from": -14, "to": -9, "points": [190, -100, 190, -90, 190, -72.39579391479492, -20, -72.39579391479492, -20, -100, -20, -110], "fromSpot": "BottomSide", "toSpot": "BottomSide", "text": "No", "color": "#ff3333", "thickness": 2, "dir": 1 },
        { "from": -14, "to": -7, "points": [190, -180, 190, -190, 190, -440, 223.5955, -440, 257.191, -440, 267.191, -440], "fromSpot": "TopSide", "toSpot": "LeftSide", "color": "#7d33ff", "dir": 1 },
        { "from": -7, "to": -8, "points": [337.191, -400, 337.191, -390, 337.191, -370, 337.191, -370, 337.191, -350, 337.191, -340], "dir": 1 },
        { "from": -8, "to": -15, "points": [360.5243333333333, -260, 360.5243333333333, -250, 360.5243333333333, -157.5, 380.26216666666664, -157.5, 400, -157.5, 410, -157.5], "toSpot": "LeftSide", "dir": 1 },
        { "from": -8, "to": -18, "points": [313.85766666666666, -260, 313.85766666666666, -242, 313.85766666666666, -82.5, 356.92883333333333, -82.5, 400, -82.5, 410, -82.5], "toSpot": "LeftSide", "fromSpot": "BottomSide", "text": "", "dir": 1 },
        { "from": -8, "to": -7, "points": [407.191, -300, 417.191, -300, 443, -300, 443, -440, 417.191, -440, 407.191, -440], "toSpot": "RightSide", "fromSpot": "RightSide", "text": "No\n", "color": "#ff3333", "dir": 1 },
        { "from": -15, "to": -13, "points": [530, -157.5, 540, -157.5, 563.3333740234375, -157.5, 563.3333740234375, -557.7291679382324, 182.66668701171875, -557.7291679382324, 182.66668701171875, -440, 140, -440, 130, -440], "toSpot": "RightSide", "fromSpot": "RightSide", "dash": [2, 4], "dir": 1 },
        { "from": -18, "to": -12, "points": [530, -82.5, 540, -82.5, 587.3333740234375, -82.5, 587.3333740234375, -579.0624961853027, -238.66665649414062, -579.0624961853027, -238.66665649414062, -440, -180, -440, -170, -440], "fromSpot": "RightSide", "toSpot": "LeftSide", "dash": [2, 4], "dir": 1 }
      ]
    };

  </script>
  <script>
    /*
    图表
    createtime: 2024/4/20
    */
    (function ($) {
      $.fn.yqDiagram = function (opts, param) {
        if (typeof opts == 'string') {
          return $.fn.yqDiagram.methods[opts](this, param);
        }

        var $this = this;
        var opt;
        if ($this.data("yqtype") == "yqDiagram") {
          destroy($this);
        }

        opt = $.extend({}, $.fn.yqDiagram.defaults, opts);
        var $uniqueID = $this.attr("ID");
        if ($uniqueID === undefined || $uniqueID === null || $uniqueID == "") {
          $uniqueID = $.getUniqueID();
          $this.attr("ID", $uniqueID);
        }

        if (opt.width === undefined && $this.width() > 0) {
          opt.width = $this.width();
        }
        else if (typeof opt.width != "undefined") {
          $this.width(opt.width);
        }

        if (opt.height === undefined && $this.height() > 0) {
          opt.height = $this.height();
        }
        else if (typeof opt.height != "undefined") {
          $this.height(opt.height);
        }

        opt._flowstate = {};

        //变量信息
        $this.data("dagram_opt", opt);
        $this.data("yqtype", "yqDiagram");

        opt.beforBindingTitle.call($this, function () {
          init($this);
        });

      };

      $.fn.yqDiagram.defaults = {
        value: {
          "nodes": [],
          "link": []
        },
        isEdit: true,
        beforBindingTitle: function (callback) {
          callback();
        }, //标题绑定前
        afterBindingTitle: function () { },   //标题绑定后
      };

      $.fn.yqDiagram.methods = {
        options: function (jq) {
          return options(jq);
        },
        resize: function (jq, res) {
          var opt = options(jq);
          resize(jq, res);
        },
        isEdit: function (jq, tf) {
          isEdit(jq, tf);
        },
        destroy: function (jq) {
          var $object = jq;
          destroy($object);
        },
        setValue: function (jq, val) {
          setValue(jq, val);
        },
        clear: function (jq) {
          var opt = options(jq);
          opt.diagram.clear();
        },
        getValue: function (jq) {
          return getValue(jq);
        },
      }

      function options($object) {
        return $object.data("dagram_opt");
      }

      function destroy($object) {
        var opt = options($object);

        $object.empty();

        $object.removeData("dagram_opt");
        $object.removeData("yqtype");
      }

      function resize($object, res) {
        var opt = options($object);
        var $uniqueID = $object.attr("ID");

        if (res === undefined) {
          res = {};
        }

        if (!(res.width === undefined)) {
          opt.width = res.width;
        }
        if (!(res.height === undefined)) {
          opt.height = res.height;
        }

        $object.css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });

        opt.$diagram_div.css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });
        opt.$diagram_div.find("canvas").css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });
        opt.$diagram_div.find("div").css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });
      }

      function isEdit($object, tf) {
        var opt = options($object);
        if (tf === undefined) {
          tf = opt.isEdit;
        } else {
          opt.isEdit = tf;
        }

        opt.diagram.clearSelection();
        opt.diagram.grid.visible = tf;
        opt.diagram.isReadOnly = !tf;

        // 启用用户交互，包括选择、拖拽等  
        opt.diagram.allowSelect = tf;

        opt.diagram.contextMenu = diagramMenu($object);

        var nodes = opt.diagram.nodes;
        var it_node = nodes.iterator;
        while (it_node.next()) {
          var node = it_node.value;
          var data = node.data;
          //console.log(node.data, node);
          if (data.isGroup) {
            if (node.category == "base") {
              node.contextMenu = group_contextMenu($object); //菜单
              node.selectionAdornmentTemplate = group_selectionAdornmentTemplate($object);//选中时
            }
          } else {
            if (node.category == "block") {
              node.contextMenu = block_ContextMenu($object); //菜单
              node.selectionAdornmentTemplate = block_selectionAdornmentTemplate($object);//选中时
            } else if (node.category == "menu") {
              node.contextMenu = menu_ContextMenu($object); //菜单
              //node.selectionAdornmentTemplate = block_selectionAdornmentTemplate($object);//选中时
              if (!tf) {
                node.cursor = "pointer";
                node.click = function (e, obj) {
                  var data = obj.part.data;
                  console.log("Double-clicked node data:", data);
                  window.open(data.url);
                };
              } else {
                node.cursor = "auto";
                node.click = null;
              }
            }
          }
        }

        var links = opt.diagram.links;
        var it = links.iterator;
        while (it.next()) {
          var link = it.value;
          //link.contextMenu = linkMenu($object);
          link.selectionAdornmentTemplate = link_selectionAdornmentTemplate($object);
          link.contextMenu = link_contextMenu($object);
        }
      }

      function form_load($object) {
        var opt = options($object);
        resize($object);
        setValue($object);
        isEdit($object);

        opt.afterBindingTitle.call($object);
      }

      function init($object) {
        var opt = options($object);
        var $uniqueID = $object.attr("ID");

        opt.$ = go.GraphObject.make;

        // Colors are predefined to allow easy manipulation of themes
        opt.colors = {
          red: '#ff3333',
          blue: '#3358ff',
          green: '#25ad23',
          magenta: '#d533ff',
          purple: '#7d33ff',
          orange: '#ff6233',
          brown: '#8e571e',
          white: '#ffffff',
          black: '#000000',
          beige: '#fffcd5',
          extralightblue: '#d5ebff',
          extralightred: '#f2dfe0',
          lightblue: '#a5d2fa',
          lightgray: '#cccccc',
          lightgreen: '#b3e6b3',
          lightred: '#fcbbbd',
        };

        var diagram_div = $uniqueID + "_Div";

        var html_ = '<div id="' + diagram_div + '" style="overflow: ;flex-grow: 1; border: none; position: relative;">';
        html_ += '</div>';

        $object.css({
          "width": "100%",
          "display": "flex",
          "justify-content": "space-between",
          "background-color": "transparent"
        });

        opt.$diagram_div = $(html_);
        $object.append(opt.$diagram_div);

        opt.diagram = new go.Diagram(diagram_div, {
          "contentAlignment": go.Spot.TopLeft,
          "grid": opt.$(go.Panel,
            'Grid', // a simple 10x10 grid
            opt.$(go.Shape, 'LineH', { stroke: 'lightgray', strokeWidth: 0.5 }),
            opt.$(go.Shape, 'LineV', { stroke: 'lightgray', strokeWidth: 0.5 })
          ),
          'draggingTool.isGridSnapEnabled': true,
          "handlesDragDropForTopLevelParts": true,
          "mouseDrop": function (e) {
            // when the selection is dropped in the diagram's background,
            // make sure the selected Parts no longer belong to any Group
            var ok = e.diagram.commandHandler.addTopLevelParts(e.diagram.selection, true);
            if (!ok) e.diagram.currentTool.doCancel();
          },
          commandHandler: opt.$(DrawCommandHandler), // support offset copy-and-paste

          //点击创建节点
          //'clickCreatingTool.archetypeNodeData': { "category": "block" , text: 'NEW NODE' }, // create a new node by double-clicking in background

          PartCreated: function (e) {
            var node = e.subject; // the newly inserted Node -- now need to snap its location to the grid
            node.location = node.location.copy().snapToGridPoint(e.diagram.grid.gridOrigin, e.diagram.grid.gridCellSize);
            setTimeout(function () {
              // and have the user start editing its text
              e.diagram.commandHandler.editTextBlock();
            }, 20);
          },
          'commandHandler.archetypeGroupData': { isGroup: true, text: 'NEW GROUP' },
          SelectionGrouped: function (e) {
            var group = e.subject;
            setTimeout(function () {
              // and have the user start editing its text
              e.diagram.commandHandler.editTextBlock();
            });
          },
          LinkRelinked: function (e) {
            // re-spread the connections of other links connected with both old and new nodes
            var oldnode = e.parameter.part;
            oldnode.invalidateConnectedLinks();
            var link = e.subject;
            if (e.diagram.toolManager.linkingTool.isForwards) {
              link.toNode.invalidateConnectedLinks();
            } else {
              link.fromNode.invalidateConnectedLinks();
            }
          },
          'undoManager.isEnabled': true,
        });

        // Node template block
        opt.diagram.nodeTemplateMap.add("block", opt.$(go.Node,
          'Auto',
          {
            locationSpot: go.Spot.Center,
            locationObjectName: 'SHAPE',
            desiredSize: new go.Size(120, 60),
            minSize: new go.Size(40, 40),
            resizable: true,
            resizeCellSize: new go.Size(20, 20),

            toolTip: block_NodeToolTip($object), //提示框
            //contextMenu: block_ContextMenu($object), //右键菜单
            //selectionAdornmentTemplate: block_selectionAdornmentTemplate($object)//选中时
          },
          new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify),
          new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),
          opt.$(go.Shape,
            {
              // the border
              name: 'SHAPE',
              fill: opt.colors.white,
              cursor: 'pointer',
              portId: '',
              // fromLinkable: true,
              // toLinkable: true,
              // fromLinkableDuplicates: true,
              // toLinkableDuplicates: true,
              // fromSpot: go.Spot.AllSides,
              // toSpot: go.Spot.AllSides,
            },
            new go.Binding('figure'),
            new go.Binding('fill'),
            new go.Binding('stroke', 'color'),
            new go.Binding('strokeWidth', 'thickness'),
            new go.Binding('strokeDashArray', 'dash')
          ),
          opt.$(go.Shape, { width: 100, height: 40, strokeWidth: 0, fill: 'transparent' }),
          opt.$(go.TextBlock,
            {
              margin: 1,
              textAlign: 'center',
              overflow: go.TextOverflow.Ellipsis,
              editable: true
            },
            new go.Binding('text').makeTwoWay(),
            new go.Binding('stroke', 'color')
          ),
          // four named ports, one on each side:
          node_makePort($object, "T", go.Spot.Top, go.Spot.Top, true, true),
          node_makePort($object, "L", go.Spot.Left, go.Spot.Left, true, true),
          node_makePort($object, "R", go.Spot.Right, go.Spot.Right, true, true),
          node_makePort($object, "B", go.Spot.Bottom, go.Spot.Bottom, true, true)
        ));

        // Node template menu
        opt.diagram.nodeTemplateMap.add("menu", opt.$(go.Node, "Auto",
          {
            minSize: new go.Size(25, 40),
            resizable: true,
            resizeObjectName: "Shape",
          },
          new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify),
          new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),
          opt.$(go.Shape, "Rectangle", {
            fill: opt.colors.white,
            stroke: "transparent",
          },
            new go.Binding('fill'),
          ),
          opt.$(go.Panel, "Vertical",
            {
              defaultAlignment: go.Spot.Center,
            },
            opt.$(go.Picture,
              {
                margin: new go.Margin(0, 0, 3, 0),
                width: 20,
                height: 20,
              },
              new go.Binding("source", "icon")),
            opt.$(go.TextBlock,
              {
                margin: new go.Margin(3, 0, 0, 0),
                alignment: go.Spot.Center,
                editable: true
              },
              new go.Binding('text').makeTwoWay(),
              new go.Binding('stroke', 'color')

            )
          ),
          // four named ports, one on each side:
          node_makePort($object, "T", go.Spot.Top, go.Spot.TopSide, true, true),
          node_makePort($object, "L", go.Spot.Left, go.Spot.LeftSide, true, true),
          node_makePort($object, "R", go.Spot.Right, go.Spot.RightSide, true, true),
          node_makePort($object, "B", go.Spot.Bottom, go.Spot.BottomSide, true, true)
        )
        );

        // Group template  
        opt.diagram.groupTemplateMap.add("base",
          opt.$(go.Group, 'Spot',
            {
              //locationObjectName: 'SHAPE',
              //resizable: true,
              layerName: 'Background',
              ungroupable: true,
              locationSpot: go.Spot.Center,
              selectionObjectName: 'BODY',
              computesBoundsAfterDrag: true,
              handlesDragDropForMembers: true,

              //鼠标拖动
              mouseDrop: function (e, grp) {
                return group_mouseDrop($object, e, grp);
              },
              avoidable: false,

              //选中
              selectionAdornmentTemplate: group_selectionAdornmentTemplate($object),
              //弹出菜单
              contextMenu: group_contextMenu($object),
            },
            new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify),
            //new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),
            opt.$(go.Panel,
              'Auto',
              {
                name: 'BODY',
              },
              opt.$(go.Shape,
                {
                  parameter1: 10,
                  fill: opt.colors.white,
                  strokeWidth: 2,
                  cursor: 'pointer',
                  fromLinkable: true,
                  toLinkable: true,
                  fromLinkableDuplicates: true,
                  toLinkableDuplicates: true,
                  fromSpot: go.Spot.AllSides,
                  toSpot: go.Spot.AllSides,
                },
                new go.Binding('fill'),
                new go.Binding('stroke', 'color'),
                new go.Binding('strokeWidth', 'thickness'),
                new go.Binding('strokeDashArray', 'dash')
              ),
              opt.$(go.Placeholder, { background: 'transparent', margin: 20 })
            ),
            opt.$(go.TextBlock,
              {
                alignment: go.Spot.Top,
                alignmentFocus: go.Spot.Bottom,
                font: 'bold 12pt sans-serif',
                editable: true,
              },
              new go.Binding('text'),
              new go.Binding('stroke', 'color')
            )
          ));

        // Link template
        opt.diagram.linkTemplateMap.add("", opt.$(go.Link,
          {
            layerName: 'Foreground',
            routing: go.Routing.AvoidsNodes,
            corner: 10,
            fromShortLength: 10,
            toShortLength: 15, // assume arrowhead at "to" end, need to avoid bad appearance when path is thick
            relinkableFrom: true,
            relinkableTo: true,
            reshapable: true,
            resegmentable: true,

            selectionAdornmentTemplate: link_selectionAdornmentTemplate($object),
            contextMenu: link_contextMenu($object),
          },
          new go.Binding('fromSpot', 'fromSpot', go.Spot.parse),
          new go.Binding('toSpot', 'toSpot', go.Spot.parse),
          new go.Binding('fromShortLength', 'dir', function (dir) { return (dir >= 1 ? 10 : 0); }),
          new go.Binding('toShortLength', 'dir', function (dir) { return (dir >= 1 ? 10 : 0); }),
          new go.Binding('points').makeTwoWay(), // TwoWay due to user reshaping with LinkReshapingTool

          opt.$(go.Shape, { strokeWidth: 2 },
            new go.Binding('stroke', 'color'),
            new go.Binding('strokeWidth', 'thickness'),
            new go.Binding('strokeDashArray', 'dash')),
          opt.$(go.Shape, // custom arrowheads to create the lifted effect
            {
              segmentIndex: 0,
              segmentOffset: new go.Point(15, 0),
              segmentOrientation: go.Orientation.Along,
              alignmentFocus: go.Spot.Right,
              figure: 'circle',
              width: 10,
              strokeWidth: 0,
            },
            new go.Binding('fill', 'color'),
            new go.Binding('visible', 'dir', function (dir) { return dir === 1; })
          ),
          opt.$(go.Shape,
            {
              segmentIndex: -1,
              segmentOffset: new go.Point(-10, 6),
              segmentOrientation: go.Orientation.Plus90,
              alignmentFocus: go.Spot.Right,
              figure: 'triangle',
              width: 12,
              height: 12,
              strokeWidth: 0,
            },
            new go.Binding('fill', 'color'),
            new go.Binding('visible', 'dir', function (dir) { return dir >= 1; }),
            new go.Binding('width', 'thickness', function (t) { return 7 + 3 * t; }), // custom arrowhead must scale with the size of the while
            new go.Binding('height', 'thickness', function (t) { return 7 + 3 * t; }), // while remaining centered on line
            new go.Binding('segmentOffset', 'thickness', function (t) { return new go.Point(-15, 4 + 1.5 * t); })
          ),
          opt.$(go.Shape,
            {
              segmentIndex: 0,
              segmentOffset: new go.Point(15, -6),
              segmentOrientation: go.Orientation.Minus90,
              alignmentFocus: go.Spot.Right,
              figure: 'triangle',
              width: 12,
              height: 12,
              strokeWidth: 0,
            },
            new go.Binding('fill', 'color'),
            new go.Binding('visible', 'dir', function (dir) { return dir === 2; }),
            new go.Binding('width', 'thickness', function (t) { return 7 + 3 * t; }),
            new go.Binding('height', 'thickness', function (t) { return 7 + 3 * t; }),
            new go.Binding('segmentOffset', 'thickness', function (t) { return new go.Point(-15, 4 + 1.5 * t); })
          ),
          opt.$(go.TextBlock,
            { alignmentFocus: new go.Spot(0, 1, -4, 0), editable: true },
            new go.Binding('text').makeTwoWay(), // TwoWay due to user editing with TextEditingTool
            new go.Binding('stroke', 'color')
          )
        ));

        form_load($object);
      }

      function diagramMenu($object) {
        var opt = options($object);
        var menu = null;
        if (opt.isEdit) {
          menu =
            opt.$("ContextMenu",
              makeLinkButton($object, "添加菜单",
                function (e, obj) {
                  addMenuNode($object, e, obj);
                }),
              makeLinkButton($object, "添加文本",
                function (e, obj) {
                  addLabelNode($object, e, obj);
                }),

              makeLinkButtonLine($object),
              makeLinkButton($object, "clear",
                function (e, obj) {
                  opt.diagram.clear();
                }),
              makeLinkButton($object, "添加组",
                function (e, obj) {
                  addGroup($object, e, obj);
                }),
            );
        }

        return menu;
      }

      function menu_ContextMenu($object) {
        var opt = options($object);

        if (opt.isEdit) {
          return opt.$('ContextMenu',
            LightFillButtons($object),
            DarkColorButtons($object),
            makeLinkButton($object, "编辑",
              function (e, obj) {
                //e.diagram.commandHandler.deleteSelection();
              }),
            makeLinkButton($object, "删除", function (e, obj) {
              e.diagram.commandHandler.deleteSelection()
            }),
          );
        } else {
          return null;
        }

        var menu = null;
        if (opt.isEdit) {
          var menu =
            opt.$("ContextMenu",
              makeLinkButton($object, "添加菜单",
                function (e, obj) {
                  addMenuNode($object, e, obj);
                }),
              makeLinkButton($object, "添加文本",
                function (e, obj) {
                  addLabelNode($object, e, obj);
                }),
              makeLinkButtonLine($object),
              makeLinkButton($object, "删除",
                function (e, obj) { e.diagram.commandHandler.deleteSelection() }),
              makeLinkButton($object, "编辑",
                function (e, node) {
                  editNode($object, e, node);
                }),
            );
        }

        return menu;
      }

      function makeLinkButton($object, text, action) {
        var opt = options($object);
        return opt.$("ContextMenuButton",
          opt.$(go.Panel, 'Horizontal', {
            defaultAlignment: go.Spot.Center,
          },
            opt.$(go.TextBlock, text),
            {
              height: 23,
              //stroke: 'lightgray',
              //fill: color,
              defaultAlignment: go.Spot.Center,
              margin: 1,
              background: 'transparent',
              mouseEnter: function (e, shape) { return (shape.stroke = 'dodgerblue'); },
              mouseLeave: function (e, shape) { return (shape.stroke = 'lightgray'); },
              click: action,
              contextClick: action,
            }));
      }

      function makeLinkButtonLine($object) {
        var opt = options($object);
        return opt.$(go.Shape, "Rectangle", {
          height: 1,
          stretch: go.GraphObject.Horizontal,
          stroke: "#C0C0C0",
          fill: "#C0C0C0"
        });
      }

      function addGroup($object, e, obj) {
        var opt = options($object);

        var nodedata = {
          "isGroup": true,
          "category": "base",
          "text": "New Group",
          "key": $.getShortID(),
          "loc": getAddLoc($object, e),
          "fill": "#fcbbbd",
          "thickness": 1,
          "color": "#000000"
        };

        opt.diagram.model.addNodeData(nodedata);
        var newnode = opt.diagram.findNodeForData(nodedata);
        opt.diagram.select(newnode);
        newnode.contextMenu = menu_ContextMenu($object);
      }

      function addMenuNode($object, e, obj) {
        var opt = options($object);

        var nodedata = {
          "category": "menu",
          "key": $.getShortID(),
          "text": "系统管理",
          "icon": "/images/menu.png",
          "url": "http://www.uieip.com/",
          "loc": getAddLoc($object, e),
          "size": "65 50"
        };

        opt.diagram.model.addNodeData(nodedata);
        var newnode = opt.diagram.findNodeForData(nodedata);
        opt.diagram.select(newnode);
        newnode.contextMenu = menu_ContextMenu($object);
      }

      function addLabelNode($object, e, obj) {
        var opt = options($object);

        var nodedata = {
          "category": "block",
          "text": "New Label",
          "key": $.getShortID(),
          "loc": getAddLoc($object, e),
          "color": "#3358ff",
          "fill": "white",
          "figure": "Rectangle",
          "thickness": 1
        };

        opt.diagram.model.addNodeData(nodedata);
        var newnode = opt.diagram.findNodeForData(nodedata);
        opt.diagram.select(newnode);

        newnode.contextMenu = block_ContextMenu($object); //菜单
        newnode.selectionAdornmentTemplate = block_selectionAdornmentTemplate($object);//选中时
      }

      function getAddLoc($object, e) {
        var opt = options($object);
        var docPoint = e.documentPoint.copy().offset(-45, -40);
        var s_obj = opt.diagram.findPartAt(docPoint);
        if (s_obj) {
          if (s_obj instanceof go.Node) {
            docPoint = docPoint.offset(40, 0);
          } else if (s_obj instanceof go.Link) {
            docPoint = docPoint.offset(30, 0);
          }
        }
        return docPoint.x + " " + docPoint.y;
      }


      //block 提示框
      function block_NodeToolTip($object) {
        var opt = options($object);

        return opt.$('ToolTip', // show some detailed information
          opt.$(go.Panel,
            'Vertical',
            { maxSize: new go.Size(200, NaN) }, // limit width but not height
            opt.$(go.TextBlock, { font: 'bold 10pt sans-serif', textAlign: 'center' }, new go.Binding('text')),
            opt.$(go.TextBlock, { font: '10pt sans-serif', textAlign: 'center' }, new go.Binding('text', 'details'))
          )
        );
      }

      //block右键菜单
      function block_ContextMenu($object) {
        var opt = options($object);

        if (opt.isEdit) {
          return opt.$('ContextMenu',
            opt.$('ContextMenuButton',
              opt.$(go.Panel, 'Horizontal',
                FigureButton($object, 'Rectangle'),
                FigureButton($object, 'RoundedRectangle'),
                FigureButton($object, 'Ellipse'),
                FigureButton($object, 'Diamond'))
            ),
            opt.$('ContextMenuButton',
              opt.$(go.Panel, 'Horizontal',
                FigureButton($object, 'Parallelogram2'),
                FigureButton($object, 'ManualOperation'),
                FigureButton($object, 'Procedure'),
                FigureButton($object, 'Cylinder1'))
            ),
            opt.$('ContextMenuButton',
              opt.$(go.Panel, 'Horizontal',
                FigureButton($object, 'Terminator'),
                FigureButton($object, 'CreateRequest'),
                FigureButton($object, 'Document'),
                FigureButton($object, 'TriangleDown'))
            ),
            LightFillButtons($object),
            DarkColorButtons($object),
            StrokeOptionsButtons($object),
            makeLinkButton($object, "删除",
              function (e, obj) { e.diagram.commandHandler.deleteSelection() }),
          );
        } else {
          return null;
        }
      }

      //block 选中时
      function block_selectionAdornmentTemplate($object) {
        var opt = options($object);

        if (opt.isEdit) {
          return opt.$(go.Adornment,
            'Spot',
            opt.$(go.Placeholder, { padding: 7 }),
            block_makeArrowButton($object, go.Spot.Top, 'TriangleUp'),
            block_makeArrowButton($object, go.Spot.Left, 'TriangleLeft'),
            block_makeArrowButton($object, go.Spot.Right, 'TriangleRight'),
            block_makeArrowButton($object, go.Spot.Bottom, 'TriangleDown'),
            CMButton($object, { alignment: new go.Spot(0.75, 0) })
          );
        } else {
          return null;
        }
      }

      // Node begin
      //复制添加
      function block_makeArrowButton($object, spot, fig) {
        var opt = options($object);

        var maker = function (e, shape) {
          e.handled = true;
          e.diagram.model.commit(function (m) {
            var selnode = shape.part.adornedPart;
            // create a new node in the direction of the spot
            var p = new go.Point().setRectSpot(selnode.actualBounds, spot);
            p.subtract(selnode.location);
            p.scale(2, 2);
            p.x += Math.sign(p.x) * 30;
            p.y += Math.sign(p.y) * 30;
            p.add(selnode.location);
            p.snapToGridPoint(e.diagram.grid.gridOrigin, e.diagram.grid.gridCellSize);
            // make the new node a copy of the selected node
            var nodedata = m.copyNodeData(selnode.data);
            // add to same group as selected node
            m.setGroupKeyForNodeData(nodedata, m.getGroupKeyForNodeData(selnode.data));
            m.addNodeData(nodedata); // add to model
            // create a link from the selected node to the new node
            var linkdata = { from: selnode.key, to: m.getKeyForNodeData(nodedata) };
            m.addLinkData(linkdata); // add to model
            // move the new node to the computed location, select it, and start to edit it
            var newnode = e.diagram.findNodeForData(nodedata);
            newnode.location = p;
            e.diagram.select(newnode);
            setTimeout(function () {
              e.diagram.commandHandler.editTextBlock();
            }, 20);
          });
        };
        return opt.$(go.Shape, {
          figure: fig,
          alignment: spot,
          alignmentFocus: spot.opposite(),
          width: spot.equals(go.Spot.Top) || spot.equals(go.Spot.Bottom) ? 25 : 18,
          height: spot.equals(go.Spot.Top) || spot.equals(go.Spot.Bottom) ? 18 : 25,
          fill: 'orange',
          stroke: opt.colors.white,
          strokeWidth: 4,
          mouseEnter: function (e, shape) { return (shape.fill = 'dodgerblue'); },
          mouseLeave: function (e, shape) { return (shape.fill = 'orange'); },
          isActionable: true, // needed because it's in an Adornment
          click: maker,
          contextClick: maker,
        });
      }

      //... 按钮，启动编辑页面
      function CMButton($object, _options) {
        var opt = options($object);

        return opt.$(go.Shape,
          {
            fill: 'orange',
            stroke: 'rgba(0, 0, 0, 0)',
            strokeWidth: 15,
            background: 'transparent',
            geometryString: 'F1 M0 0 b 0 360 -4 0 4 z M10 0 b 0 360 -4 0 4 z M20 0 b 0 360 -4 0 4', // M10 0 A2 2 0 1 0 14 10 M20 0 A2 2 0 1 0 24 10,
            isActionable: true,
            cursor: 'context-menu',
            mouseEnter: function (e, shape) { return (shape.fill = 'dodgerblue'); },
            mouseLeave: function (e, shape) { return (shape.fill = 'orange'); },
            click: function (e, shape) {
              e.diagram.commandHandler.showContextMenu(shape.part.adornedPart);
            },
          },
          _options || {}
        );
      }


      function ClickFunction($object, propname, value) {
        var opt = options($object);

        return function (e, obj) {
          e.handled = true; // don't let the click bubble up
          e.diagram.model.commit(function (m) {
            m.set(obj.part.adornedPart.data, propname, value);
          });
        };
      }

      // Create a context menu button for setting a data property with a color value.
      function ColorButton($object, color, propname) {
        var opt = options($object);

        if (!propname) propname = 'color';
        return opt.$(go.Shape, {
          width: 16,
          height: 16,
          stroke: 'lightgray',
          fill: color,
          margin: 1,
          background: 'transparent',
          mouseEnter: function (e, shape) { return (shape.stroke = 'dodgerblue'); },
          mouseLeave: function (e, shape) { return (shape.stroke = 'lightgray'); },
          click: ClickFunction($object, propname, color),
          contextClick: ClickFunction($object, propname, color),
        });
      }

      function LightFillButtons($object) {
        var opt = options($object);

        // used by multiple context menus
        return [
          opt.$('ContextMenuButton',
            opt.$(go.Panel,
              'Horizontal',
              ColorButton($object, opt.colors.white, 'fill'),
              ColorButton($object, opt.colors.beige, 'fill'),
              ColorButton($object, opt.colors.extralightblue, 'fill'),
              ColorButton($object, opt.colors.extralightred, 'fill')
            )
          ),
          opt.$('ContextMenuButton',
            opt.$(go.Panel,
              'Horizontal',
              ColorButton($object, opt.colors.lightgray, 'fill'),
              ColorButton($object, opt.colors.lightgreen, 'fill'),
              ColorButton($object, opt.colors.lightblue, 'fill'),
              ColorButton($object, opt.colors.lightred, 'fill')
            )
          ),
        ];
      }

      function DarkColorButtons($object) {
        var opt = options($object);
        // used by multiple context menus
        return [
          opt.$('ContextMenuButton',
            opt.$(go.Panel, 'Horizontal',
              ColorButton($object, opt.colors.black),
              ColorButton($object, opt.colors.green),
              ColorButton($object, opt.colors.blue),
              ColorButton($object, opt.colors.red))
          ),
          opt.$('ContextMenuButton',
            opt.$(go.Panel, 'Horizontal',
              ColorButton($object, opt.colors.white),
              ColorButton($object, opt.colors.magenta),
              ColorButton($object, opt.colors.purple),
              ColorButton($object, opt.colors.orange))
          ),
        ];
      }

      // Create a context menu button for setting a data property with a stroke width value.
      function ThicknessButton($object, sw, propname) {
        var opt = options($object);

        if (!propname) propname = 'thickness';
        return opt.$(go.Shape, 'LineH', {
          width: 16,
          height: 16,
          strokeWidth: sw,
          margin: 1,
          background: 'transparent',
          mouseEnter: function (e, shape) { return (shape.background = 'dodgerblue'); },
          mouseLeave: function (e, shape) { return (shape.background = 'transparent'); },
          click: ClickFunction($object, propname, sw),
          contextClick: ClickFunction($object, propname, sw),
        });
      }

      // Create a context menu button for setting a data property with a stroke dash Array value.
      function DashButton($object, dash, propname) {
        var opt = options($object);

        if (!propname) propname = 'dash';
        return opt.$(go.Shape, 'LineH', {
          width: 24,
          height: 16,
          strokeWidth: 2,
          strokeDashArray: dash,
          margin: 1,
          background: 'transparent',
          mouseEnter: function (e, shape) { return (shape.background = 'dodgerblue'); },
          mouseLeave: function (e, shape) { return (shape.background = 'transparent'); },
          click: ClickFunction($object, propname, dash),
          contextClick: ClickFunction($object, propname, dash),
        });
      }

      function StrokeOptionsButtons($object) {
        var opt = options($object);

        // used by multiple context menus
        return [
          opt.$('ContextMenuButton', opt.$(go.Panel, 'Horizontal',
            ThicknessButton($object, 1),
            ThicknessButton($object, 2),
            ThicknessButton($object, 3),
            ThicknessButton($object, 4),
            ThicknessButton($object, 0))),
          opt.$('ContextMenuButton', opt.$(go.Panel, 'Horizontal',
            DashButton($object, null),
            DashButton($object, [2, 4]),
            DashButton($object, [4, 4]))),
        ];
      }

      function FigureButton($object, fig, propname) {
        var opt = options($object);

        if (!propname) propname = 'figure';
        return opt.$(go.Shape, {
          width: 32,
          height: 32,
          scale: 0.5,
          fill: 'lightgray',
          figure: fig,
          margin: 1,
          background: 'transparent',
          mouseEnter: function (e, shape) { return (shape.fill = 'dodgerblue'); },
          mouseLeave: function (e, shape) { return (shape.fill = 'lightgray'); },
          click: ClickFunction($object, propname, fig),
          contextClick: ClickFunction($object, propname, fig),
        });
      }

      //节点连接端点的样式
      function node_makePort($object, name, align, spot, output, input) {
        var opt = options($object);

        var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
        // the port is basically just a transparent rectangle that stretches along the side of the node,
        // and becomes colored when the mouse passes over it
        return opt.$(go.Shape,
          {
            fill: "transparent",  // changed to a color in the mouseEnter event handler
            strokeWidth: 0,  // no stroke
            width: horizontal ? NaN : 4,  // if not stretching horizontally, just 8 wide
            height: !horizontal ? NaN : 4,  // if not stretching vertically, just 8 tall
            alignment: align,  // align the port on the main Shape
            stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical),
            portId: name,  // declare this object to be a "port"
            fromSpot: spot,  // declare where links may connect at this port
            fromLinkable: output,  // declare whether the user may draw links from here
            toSpot: spot,  // declare where links may connect at this port
            toLinkable: input,  // declare whether the user may draw links to here
            cursor: "pointer",  // show a different cursor to indicate potential link point
            mouseEnter: function (e, port) {  // the PORT argument will be this Shape
              if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
            },
            mouseLeave: function (e, port) { return port.fill = "transparent"; }
          });
      }

      // Node end

      // group begin       
      //鼠标拖动
      function group_mouseDrop($object, e, grp) {
        var opt = options($object);
        var ok = grp.addMembers(grp.diagram.selection, true);
        if (!ok) grp.diagram.currentTool.doCancel();
      }

      //选中
      function group_selectionAdornmentTemplate($object) {
        var opt = options($object);

        return opt.$(go.Adornment,
          'Spot',
          opt.$(go.Panel, 'Auto', opt.$(go.Shape, { fill: null, stroke: 'dodgerblue', strokeWidth: 3 }), opt.$(go.Placeholder, { margin: 1.5 })),
          CMButton($object, { alignment: go.Spot.TopRight, alignmentFocus: go.Spot.BottomRight })
        );
      }

      //弹出菜单
      function group_contextMenu($object) {
        var opt = options($object);
        if (opt.isEdit) {
          return opt.$('ContextMenu',
            LightFillButtons($object),
            DarkColorButtons($object),
            StrokeOptionsButtons($object)
          );
        } else {
          return null;
        }
      }
      // group end

      //link begin
      //link 选择
      function link_selectionAdornmentTemplate($object) {
        var opt = options($object);

        if (!opt.isEdit) {
          return null;
        }

        return opt.$(go.Adornment, // use a special selection Adornment that does not obscure the link path itself
          opt.$(go.Shape,
            {
              // this uses a pathPattern with a gap in it, in order to avoid drawing on top of the link path Shape
              isPanelMain: true,
              stroke: 'transparent',
              strokeWidth: 6,
              pathPattern: link_makeAdornmentPathPattern($object, 2), // == thickness or strokeWidth
            },
            new go.Binding('pathPattern', 'thickness', link_makeAdornmentPathPattern($object, 0))
          ),
          CMButton($object, { alignmentFocus: new go.Spot(0, 0, -6, -4) })
        );
      }

      function link_makeAdornmentPathPattern($object, w) {
        var opt = options($object);

        return opt.$(go.Shape, {
          stroke: 'dodgerblue',
          strokeWidth: 2,
          strokeCap: 'square',
          geometryString: 'M0 0 M4 2 H3 M4 ' + (w + 4).toString() + ' H3',
        });
      }

      // Link context menu
      function link_contextMenu($object) {
        var opt = options($object);

        if (!opt.isEdit) {
          return null;
        }

        return opt.$('ContextMenu',
          DarkColorButtons($object),
          StrokeOptionsButtons($object),
          opt.$('ContextMenuButton', opt.$(go.Panel, 'Horizontal', ArrowButton($object, 0), ArrowButton($object, 1), ArrowButton($object, 2))),
          opt.$('ContextMenuButton',
            opt.$(go.Panel,
              'Horizontal',
              opt.$(go.Panel,
                'Spot',
                AllSidesButton($object, false),
                SpotButton($object, go.Spot.Top, false),
                SpotButton($object, go.Spot.Left, false),
                SpotButton($object, go.Spot.Right, false),
                SpotButton($object, go.Spot.Bottom, false)
              ),
              opt.$(go.Panel,
                'Spot',
                { margin: new go.Margin(0, 0, 0, 2) },
                AllSidesButton($object, true),
                SpotButton($object, go.Spot.Top, true),
                SpotButton($object, go.Spot.Left, true),
                SpotButton($object, go.Spot.Right, true),
                SpotButton($object, go.Spot.Bottom, true)
              )
            )
          )
        );
      }

      function ArrowButton($object, num) {
        var opt = options($object);

        var geo = 'M0 0 M8 16 M0 8 L16 8  M12 11 L16 8 L12 5';
        if (num === 0) {
          geo = 'M0 0 M16 16 M0 8 L16 8';
        } else if (num === 2) {
          geo = 'M0 0 M16 16 M0 8 L16 8  M12 11 L16 8 L12 5  M4 11 L0 8 L4 5';
        }
        return opt.$(go.Shape, {
          geometryString: geo,
          margin: 2,
          background: 'transparent',
          mouseEnter: function (e, shape) { return (shape.background = 'dodgerblue'); },
          mouseLeave: function (e, shape) { return (shape.background = 'transparent'); },
          click: ClickFunction($object, 'dir', num),
          contextClick: ClickFunction($object, 'dir', num),
        });
      }

      function AllSidesButton($object, to) {
        var opt = options($object);

        var setter = function (e, shape) {
          e.handled = true;
          e.diagram.model.commit(function (m) {
            var link = shape.part.adornedPart;
            m.set(link.data, to ? 'toSpot' : 'fromSpot', go.Spot.stringify(go.Spot.AllSides));
            // re-spread the connections of other links connected with the node
            (to ? link.toNode : link.fromNode).invalidateConnectedLinks();
          });
        };
        return opt.$(go.Shape, {
          width: 12,
          height: 12,
          fill: 'transparent',
          mouseEnter: function (e, shape) { return (shape.background = 'dodgerblue'); },
          mouseLeave: function (e, shape) { return (shape.background = 'transparent'); },
          click: setter,
          contextClick: setter,
        });
      }

      function SpotButton($object, spot, to) {
        var opt = options($object);

        var ang = 0;
        var side = go.Spot.RightSide;
        if (spot.equals(go.Spot.Top)) {
          ang = 270;
          side = go.Spot.TopSide;
        } else if (spot.equals(go.Spot.Left)) {
          ang = 180;
          side = go.Spot.LeftSide;
        } else if (spot.equals(go.Spot.Bottom)) {
          ang = 90;
          side = go.Spot.BottomSide;
        }
        if (!to) ang -= 180;
        var setter = function (e, shape) {
          e.handled = true;
          e.diagram.model.commit(function (m) {
            var link = shape.part.adornedPart;
            m.set(link.data, to ? 'toSpot' : 'fromSpot', go.Spot.stringify(side));
            // re-spread the connections of other links connected with the node
            (to ? link.toNode : link.fromNode).invalidateConnectedLinks();
          });
        };
        return opt.$(go.Shape, {
          alignment: spot,
          alignmentFocus: spot.opposite(),
          geometryString: 'M0 0 M12 12 M12 6 L1 6 L4 4 M1 6 L4 8',
          angle: ang,
          background: 'transparent',
          mouseEnter: function (e, shape) { return (shape.background = 'dodgerblue'); },
          mouseLeave: function (e, shape) { return (shape.background = 'transparent'); },
          click: setter,
          contextClick: setter,
        });
      }
      //Link end

      function setValue($object, value) {
        var opt = options($object);

        if (!(value === undefined)) {
          if (typeof value == "string") {
            value = JSON.parse(value);
          }
          opt.value = value;
        }

        if (!(typeof opt.value === "object")) {
          opt.value = {
            nodes: [],
            links: []
          };
        }
        if (!Array.isArray(opt.value.nodes)) {
          opt.value.nodes = [];
        }
        if (!Array.isArray(opt.value.links)) {
          opt.value.links = [];
        }

        opt.diagram.model = go.Model.fromJson({
          "class": "GraphLinksModel",
          "nodeDataArray": opt.value.nodes,
          "linkDataArray": opt.value.links
        });
      }

      function getValue($object) {
        var opt = options($object);
        var value = JSON.parse(opt.diagram.model.toJson());
        var j_ = {
          nodes: value.nodeDataArray,
          links: value.linkDataArray
        };

        for (var i = 0; i < j_.nodes.length; i++) {
          var locs = j_.nodes[i].loc.split(" ");
          j_.nodes[i].loc = (locs[0].indexOf(".") > 0 ? parseFloat(locs[0]).toFixed(2) : locs[0])
            + " "
            + (locs[1].indexOf(".") > 0 ? parseFloat(locs[1]).toFixed(2) : locs[1]);
        }

        for (var i = 0; i < j_.links.length; i++) {
          var points = [];
          for (var j = 0; j < j_.links[i].points.length; j++) {
            var point = j_.links[i].points[j].toString();
            if (point.indexOf(".") > 0) {
              point = parseFloat(j_.links[i].points[j].toFixed(2));
            } else {
              point = j_.links[i].points[j];
            }
            points.push(point);
          }
          j_.links[i].points = points;
        }

        return j_;
      }
    })(jQuery);

  </script>

</body>

</html>